package cz.semjobKB.conllx.api.impl;

import cz.semjobKB.conllx.api.*;

import java.util.ArrayList;
import java.util.List;


/**
 * The Document is a root of the document tree processed by treex. Provides methods to operate with the tree.
 */
public class Document implements IDocument {

	private static final long serialVersionUID = 1L;

	/** The paragraphs. */
	private final List<Paragraph> paragraphs;

	/** The selected paragraph. */
	protected int selectedParagraph = 0;

	/** The selected sentence. */
	protected int selectedSentence = 0;

	/** The selected word. */
	protected int selectedWord = 0;

	/** The text. */
	private final String text;

	/**
	 * Instantiates a new document.
	 * 
	 * @param text
	 *            The original text
     *
     */
	public Document(final String text) {
		paragraphs = new ArrayList<Paragraph>();
		this.text = text;
	}

	public String getText() {
		return text;
	}


	public boolean hasNext(final ETextPart part) {
		if (part == ETextPart.WORD) {
			if (!isLast(ETextPart.PARAGRAPH) || !isLast(ETextPart.SENTENCE)) {
				return true;
			} else {
				Sentence currentSentence = this.getSentence(selectedParagraph, selectedSentence);
				if (selectedWord < currentSentence.getWordCount()) {
					return true;
				}
			}
		}
		if (part == ETextPart.SENTENCE) {
			if (!isLast(ETextPart.PARAGRAPH)) {
				return true;
			} else {
				Paragraph currentParagraph = this.getParagraph(selectedParagraph);
				if (selectedSentence < currentParagraph.getSentenceCount()) {
					return true;
				}
			}
		}
		if (part == ETextPart.PARAGRAPH) {
			if (selectedParagraph < this.getParagraphCount()) {
				return true;
			}
		}
		return false;
	}

	/**
	 * Checks if the text part is last. If the text part is Word, then it checks, if it is the last word in the
	 * sentence. If the text part is Sentence, then it checks if it is the last sentence in paragraph. And the last
	 * paragraph of the document.
	 * 
	 * @param part
	 *            Type of the text part
	 * @return true, if it is the last text part within its own text type
	 */
	public boolean isLast(final ETextPart part) {
		if (hasNext(part)) {
			if (part == ETextPart.WORD) {
				return (isLast(ETextPart.PARAGRAPH) && isLast(ETextPart.SENTENCE) && this.selectedWord == this
						.getSentence(selectedParagraph, selectedSentence).getWordCount() - 1);
			}
			if (part == ETextPart.SENTENCE) {
				return (isLast(ETextPart.PARAGRAPH) && selectedSentence == this.getParagraph(getParagraphCount() - 1)
						.getSentenceCount() - 1);
			}
			if (part == ETextPart.PARAGRAPH) {
				return (selectedParagraph == this.getParagraphCount() - 1);
			}
		}
		return false;
	}
	
	public ITextPart getNext(final ETextPart part) {
		if (part == ETextPart.WORD) {
			Sentence currentSentence = this.getSentence(selectedParagraph, selectedSentence);
			if (selectedWord < currentSentence.getWordCount()) {
				Word currentWord = currentSentence.getWord(selectedWord);
				selectedWord = selectedWord + 1;
				return currentWord;
			} else if (!isLast(part)) {
				selectedSentence++;
				selectedWord = 0;
				return getNext(part);
			}
		}
		if (part == ETextPart.SENTENCE) {
			Paragraph currentParagraph = this.getParagraph(selectedParagraph);
			if (selectedSentence < currentParagraph.getSentenceCount()) {
				Sentence currentSentence = currentParagraph.getSentence(selectedSentence);
				selectedSentence = selectedSentence + 1;
				return currentSentence;
			} else if (!isLast(part)) {
				selectedParagraph++;
				selectedSentence = 0;
				selectedWord = 0;
				return getNext(part);
			}
		}
		if (part == ETextPart.PARAGRAPH) {
			if (selectedParagraph < this.getParagraphCount()) {
				Paragraph currentParagraph = this.getParagraph(selectedParagraph);
				selectedParagraph = selectedParagraph + 1;
				return currentParagraph;
			}
		}
		return null;
	}

	/**
	 * Gets the paragraph on a specific position
	 * 
	 * @param paragraphPosition
	 *            the paragraph position
	 * @return the paragraph
	 */
	public Paragraph getParagraph(final int paragraphPosition) {
		return paragraphs.get(paragraphPosition);
	}

	/**
	 * Gets the sentence on a position in a specific paragraph
	 * 
	 * @param paragraphPosition
	 *            the paragraph position
	 * @param sentencePosition
	 *            the sentence position
	 * @return the sentence
	 */
	public Sentence getSentence(final int paragraphPosition, final int sentencePosition) {
		Paragraph paragraph = paragraphs.get(paragraphPosition);
		return paragraph.getSentence(sentencePosition);
	}

	/**
	 * Gets the word on a position in a specific paragraph and sentence.
	 * 
	 * @param paragraphPosition
	 *            the paragraph position
	 * @param sentencePosition
	 *            the sentence position
	 * @param wordPosition
	 *            the word position
	 * @return the word
	 */
	public Word getWord(final int paragraphPosition, final int sentencePosition, final int wordPosition) {
		Paragraph paragraph = paragraphs.get(paragraphPosition);
		if (paragraph != null) {
			Sentence sentence = paragraph.getSentence(sentencePosition);
			if (sentence != null) {
				return (Word) sentence.getWords().get(wordPosition);
			}
		}
		return null;
	}

	/**
	 * Loads the references between paragraphs and sentences. Also binds the original text with corresponding document
	 * parts.
	 */
	public void loadTextAndReferences() {
		for (int indexParagraph = 0; indexParagraph < paragraphs.size(); indexParagraph++) {
			for (int indexSentence = 0; indexSentence < paragraphs.get(indexParagraph).getSentences().size(); indexSentence++) {
				paragraphs.get(indexParagraph).getSentences().get(indexSentence).getText();
				if (indexSentence < paragraphs.get(indexParagraph).getSentences().size() - 1) {
					paragraphs.get(indexParagraph).getSentences().get(indexSentence)
							.setNextSentence(paragraphs.get(indexParagraph).getSentences().get(indexSentence + 1));
				}
			}
			paragraphs.get(indexParagraph).getText();
			if (indexParagraph < paragraphs.size() - 1) {
				paragraphs.get(indexParagraph).setNextParagraph(paragraphs.get(indexParagraph + 1));
			}
		}
		this.getText();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see cz.semjob.toolChain.api.IDocumentPartIterator#reset()
	 */
	public void reset() {
		selectedParagraph = 0;
		selectedSentence = 0;
		selectedWord = 0;

	}

	/**
	 * Gets the paragraph count.
	 * 
	 * @return the paragraph count
	 */
	public int getParagraphCount() {
		return paragraphs.size();
	}
	
	public List<IWord> getWords() {
		List<IWord> words = new ArrayList<IWord>();
		if (this != null) {
			IWord word = this.getWord(0, 0, 0);
			while (word != null) {
				// if (word.g)
				words.add(word);
				word = word.getNextWord();
			}
		}
		return words;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see cz.semjob.toolChain.api.IDocumentPart#getSentences()
	 */
	public List<ISentence> getSentences() {
		List<ISentence> sentences = new ArrayList<ISentence>();
		if (this != null) {
			ISentence sentence = this.getSentence(0, 0);
			while (sentence != null) {
				sentences.add(sentence);
				sentence = sentence.getNextSentence();
			}
		}
		return sentences;
	}
	
	/**
	 * Gets the selected paragraph.
	 * 
	 * @return the selected paragraph
	 */
	public int getSelectedParagraph() {
		return selectedParagraph;
	}

	/**
	 * Gets the selected sentence.
	 * 
	 * @return the selected sentence
	 */
	public int getSelectedSentence() {
		return selectedSentence;
	}

	/**
	 * Gets the selected word.
	 * 
	 * @return the selected word
	 */
	public int getSelectedWord() {
		return selectedWord;
	}

	/**
	 * Sets the selected paragraph.
	 * 
	 * @param selectedParagraph
	 *            the new selected paragraph
	 */
	public void setSelectedParagraph(final int selectedParagraph) {
		this.selectedParagraph = selectedParagraph;
	}

	/**
	 * Sets the selected sentence.
	 * 
	 * @param selectedSentence
	 *            the new selected sentence
	 */
	public void setSelectedSentence(final int selectedSentence) {
		this.selectedSentence = selectedSentence;
	}

	/**
	 * Sets the selected word.
	 * 
	 * @param selectedWord
	 *            the new selected word
	 */
	public void setSelectedWord(final int selectedWord) {
		this.selectedWord = selectedWord;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.lang.Object#toString()
	 */
	
	public String toString() {
		return "Document [selected=" + selectedParagraph + ", " + selectedSentence + ", " + selectedWord
				+ ", text=" + text + "]";
	}

	public <P extends IParagraph> List<P> getParagraphs() {		
		return (List<P>) paragraphs;
	}

	
	public IDocumentPart getNextDocumentPart(ETextPart part) {
		selectedParagraph++;
		return (IDocumentPart) paragraphs.get(selectedParagraph);
	}

}
